---
title: 語法對比基礎
---

本模組探討 JavaScript 和 Go 之間的基本語法差異，涵蓋變數宣告、控制流、函數、資料型別和其他核心語言結構。

## 變數宣告和型別系統

JavaScript 使用動態型別，變數型別在執行時確定，而 Go 使用靜態型別，具有型別推斷能力。

### 變數宣告方式對比

| 宣告方式 | JavaScript | Go | 說明 |
| :------- | :---------- | :-- | :--- |
| **變數宣告** | `let x = 10;` | `x := 10` | 型別推斷，區域作用域 |
| **常數宣告** | `const x = 10;` | `const x = 10` | 不可重新賦值 |
| **顯式型別宣告** | `let x: number = 10;` (TypeScript) | `var x int = 10` | 明確指定型別 |
| **零值初始化** | `let x;` (undefined) | `var x int` (0) | 預設值初始化 |
| **多重賦值** | `let a = 1, b = 2;` | `a, b := 1, 2` | 同時宣告多個變數 |
| **區塊級作用域** | `{ let x = 10; }` | `{ x := 10 }` | 限制變數作用域 |
| **函數作用域** | `var x = 10;` | `var x = 10` | 提升到函數頂部 |

### 型別推斷對比

| 特性 | JavaScript | Go | 說明 |
| :---- | :---------- | :-- | :--- |
| **數字型別** | `number` (64位浮點) | `int`, `int8`, `int16`, `int32`, `int64`, `uint`, `float32`, `float64` | Go 有精確的整數型別 |
| **字串型別** | `string` | `string` | 都支援 UTF-8 |
| **布林型別** | `boolean` | `bool` | 相同概念 |
| **陣列型別** | `Array` (動態) | `[n]T` (固定), `[]T` (切片) | Go 區分陣列和切片 |
| **物件型別** | `Object` | `struct`, `map` | Go 使用結構體作為主要資料結構 |
| **空值** | `null`, `undefined` | `nil` | Go 統一使用 nil |
| **型別檢查** | 執行時 | 編譯時 | Go 編譯時型別安全 |

<UniversalEditor title="變數宣告和賦值對比" compare={true}>
```javascript !! js
// JavaScript: 動態型別
let age = 30; // Number
let name = "Alice"; // String
let isActive = true; // Boolean
let scores = [85, 92, 78]; // Array
let person = { name: "Bob", age: 25 }; // Object

console.log(typeof age); // "number"
console.log(typeof name); // "string"
console.log(Array.isArray(scores)); // true
```

```go !! go
// Go: 靜態型別與型別推斷
package main

import "fmt"

func main() {
    age := 30 // int (型別推斷)
    name := "Alice" // string (型別推斷)
    isActive := true // bool (型別推斷)
    scores := []int{85, 92, 78} // int 切片
    person := map[string]interface{}{"name": "Bob", "age": 25} // map
    
    // Go 沒有像 JavaScript 那樣的 typeof，但可以使用反射
    fmt.Printf("age 型別: %T\n", age) // int
    fmt.Printf("name 型別: %T\n", name) // string
    fmt.Printf("scores 型別: %T\n", scores) // []int
}
```
</UniversalEditor>

## 控制流語句對比

兩種語言都支援類似的控制流結構，但語法和約定不同。

### If/Else 語句

<UniversalEditor title="If/Else 對比" compare={true}>
```javascript !! js
// JavaScript: If/Else
let temperature = 25;

if (temperature > 30) {
    console.log("天氣很熱！");
} else if (temperature > 20) {
    console.log("天氣溫暖。");
} else {
    console.log("天氣寒冷。");
}

// JavaScript 允許真值/假值
if (temperature) {
    console.log("溫度是真值");
}
```

```go !! go
// Go: If/Else
package main

import "fmt"

func main() {
    temperature := 25

    if temperature > 30 {
        fmt.Println("天氣很熱！")
    } else if temperature > 20 {
        fmt.Println("天氣溫暖。")
    } else {
        fmt.Println("天氣寒冷。")
    }

    // Go 需要顯式的布林表達式
    if temperature > 0 {
        fmt.Println("溫度是正數")
    }
}
```
</UniversalEditor>

### For 迴圈

<UniversalEditor title="For 迴圈對比" compare={true}>
```javascript !! js
// JavaScript: For 迴圈
// 傳統 for 迴圈
for (let i = 0; i < 5; i++) {
    console.log(i);
}

// For...of (用於可迭代物件)
let arr = [1, 2, 3, 4, 5];
for (let val of arr) {
    console.log(val);
}

// For...in (用於物件屬性)
let obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
    console.log(key, obj[key]);
}

// forEach 方法
arr.forEach((val, index) => {
    console.log(`索引 ${index}: ${val}`);
});
```

```go !! go
// Go: For 迴圈
package main

import "fmt"

func main() {
    // 傳統 for 迴圈（Go 的唯一迴圈結構）
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }

    // 基於 range 的 for 迴圈（類似 for...of）
    arr := []int{1, 2, 3, 4, 5}
    for i, val := range arr {
        fmt.Printf("索引 %d: %d\n", i, val)
    }

    // 遍歷 map（類似 for...in）
    obj := map[string]int{"a": 1, "b": 2, "c": 3}
    for key, val := range obj {
        fmt.Printf("%s: %d\n", key, val)
    }

    // 遍歷切片（只要索引）
    for i := range arr {
        fmt.Printf("索引: %d\n", i)
    }

    // 遍歷切片（只要值）
    for _, val := range arr {
        fmt.Printf("值: %d\n", val)
    }
}
```
</UniversalEditor>

## 函數定義和呼叫

Go 函數需要顯式的返回型別，與 JavaScript 函數的語法不同。

<UniversalEditor title="函數定義對比" compare={true}>
```javascript !! js
// JavaScript: 函數定義
// 函數宣告
function greet(name) {
    return `Hello, ${name}!`;
}

// 函數表達式
const add = function(a, b) {
    return a + b;
};

// 箭頭函數
const multiply = (a, b) => a * b;

// 帶預設參數的函數
function greetWithDefault(name = "World") {
    return `Hello, ${name}!`;
}

// 帶剩餘參數的函數
function sum(...numbers) {
    return numbers.reduce((acc, num) => acc + num, 0);
}

console.log(greet("Bob"));
console.log(add(5, 3));
console.log(multiply(4, 6));
console.log(greetWithDefault());
console.log(sum(1, 2, 3, 4, 5));
```

```go !! go
// Go: 函數定義
package main

import "fmt"

// 函數宣告
func greet(name string) string {
    return fmt.Sprintf("Hello, %s!", name)
}

// 帶多個返回值的函數
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("除零錯誤")
    }
    return a / b, nil
}

// 帶命名返回值的函數
func multiply(a, b int) (result int) {
    result = a * b
    return // 裸返回
}

// 帶可變參數的函數（類似剩餘參數）
func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

// 函數作為變數（類似函數表達式）
var add = func(a, b int) int {
    return a + b
}

func main() {
    fmt.Println(greet("Bob"))
    
    result, err := divide(10, 2)
    if err != nil {
        fmt.Println("錯誤:", err)
    } else {
        fmt.Println("結果:", result)
    }
    
    fmt.Println(multiply(4, 6))
    fmt.Println(sum(1, 2, 3, 4, 5))
    fmt.Println(add(5, 3))
}
```
</UniversalEditor>

## 資料型別和結構

Go 有豐富的內建型別和結構，與 JavaScript 有顯著差異。

### 資料型別詳細對比

| 資料型別 | JavaScript | Go | 零值 | 說明 |
| :------- | :---------- | :-- | :--- | :--- |
| **整數** | `number` | `int`, `int8`, `int16`, `int32`, `int64` | `0` | Go 有精確的整數大小 |
| **無符號整數** | 無 | `uint`, `uint8`, `uint16`, `uint32`, `uint64` | `0` | Go 支援無符號整數 |
| **浮點數** | `number` | `float32`, `float64` | `0.0` | Go 區分單雙精度 |
| **複數** | 無 | `complex64`, `complex128` | `0+0i` | Go 內建複數支援 |
| **字串** | `string` | `string` | `""` | 都支援 UTF-8 編碼 |
| **布林值** | `boolean` | `bool` | `false` | 相同概念 |
| **空值** | `null`, `undefined` | `nil` | `nil` | Go 統一使用 nil |
| **陣列** | `Array` | `[n]T` | `[0,0,0]` | Go 陣列固定大小 |
| **動態陣列** | `Array` | `[]T` (切片) | `nil` | Go 切片是動態的 |
| **物件** | `Object` | `struct` | 結構體零值 | Go 結構體有定義的結構 |
| **映射** | `Map` | `map[K]V` | `nil` | 鍵值對儲存 |
| **集合** | `Set` | 無內建 | - | Go 可用 map 模擬 |
| **函數** | `Function` | `func` | `nil` | 函數型別 |
| **通道** | 無 | `chan T` | `nil` | Go 併發通訊 |
| **介面** | 無 | `interface{}` | `nil` | Go 多型支援 |
| **指標** | 無 | `*T` | `nil` | Go 記憶體位址參照 |

<UniversalEditor title="資料型別對比" compare={true}>
```javascript !! js
// JavaScript: 資料型別和結構
// 基本型別
let number = 42;
let string = "Hello";
let boolean = true;
let nullValue = null;
let undefinedValue = undefined;

// 陣列
let array = [1, 2, 3, 4, 5];
let mixedArray = [1, "hello", true, null];

// 物件
let object = {
    name: "Alice",
    age: 30,
    isActive: true
};

// Map (ES6)
let map = new Map();
map.set("key1", "value1");
map.set("key2", "value2");

// Set (ES6)
let set = new Set([1, 2, 3, 3, 4]); // 重複項被移除

console.log(array.length);
console.log(Object.keys(object));
console.log(map.get("key1"));
console.log(set.has(3));
```

```go !! go
// Go: 資料型別和結構
package main

import "fmt"

func main() {
    // 基本型別
    var number int = 42
    var text string = "Hello"
    var boolean bool = true
    
    // 陣列（固定大小）
    var array [5]int = [5]int{1, 2, 3, 4, 5}
    var mixedArray [4]interface{} = [4]interface{}{1, "hello", true, nil}
    
    // 切片（動態陣列）
    slice := []int{1, 2, 3, 4, 5}
    slice = append(slice, 6) // 新增元素
    
    // Map
    object := map[string]interface{}{
        "name":     "Alice",
        "age":      30,
        "isActive": true,
    }
    
    // 結構體（類似物件但有定義的結構）
    type Person struct {
        Name     string
        Age      int
        IsActive bool
    }
    
    person := Person{
        Name:     "Bob",
        Age:      25,
        IsActive: true,
    }
    
    fmt.Printf("陣列長度: %d\n", len(array))
    fmt.Printf("切片長度: %d\n", len(slice))
    fmt.Printf("Map 鍵: %v\n", getMapKeys(object))
    fmt.Printf("Person: %+v\n", person)
}

// 輔助函數取得 map 鍵
func getMapKeys(m map[string]interface{}) []string {
    keys := make([]string, 0, len(m))
    for k := range m {
        keys = append(keys, k)
    }
    return keys
}
```
</UniversalEditor>

## 作用域和生命週期

兩種語言都有塊作用域，但 Go 的作用域規則更顯式和一致。

<UniversalEditor title="作用域和生命週期對比" compare={true}>
```javascript !! js
// JavaScript: 作用域和提升
function example() {
    let x = 10; // 塊作用域
    
    if (true) {
        let y = 20; // 塊作用域
        var z = 30; // 函數作用域（提升）
        console.log(x); // 10
        console.log(y); // 20
        console.log(z); // 30
    }
    
    console.log(x); // 10
    console.log(z); // 30（由於提升可存取）
    // console.log(y); // ReferenceError: y is not defined
}

// 閉包
function createCounter() {
    let count = 0;
    return function() {
        count++;
        return count;
    };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
```

```go !! go
// Go: 作用域和生命週期
package main

import "fmt"

func example() {
    x := 10 // 函數作用域
    
    if true {
        y := 20 // 塊作用域
        fmt.Println(x) // 10
        fmt.Println(y) // 20
    }
    
    fmt.Println(x) // 10
    // fmt.Println(y) // Error: undefined: y
}

// Go 中的閉包
func createCounter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    example()
    
    counter := createCounter()
    fmt.Println(counter()) // 1
    fmt.Println(counter()) // 2
}
```
</UniversalEditor>

## 錯誤處理

JavaScript 使用 try-catch 進行異常處理，而 Go 使用顯式的錯誤返回值。

<UniversalEditor title="錯誤處理對比" compare={true}>
```javascript !! js
// JavaScript: 異常處理
function divide(a, b) {
    if (b === 0) {
        throw new Error("除零錯誤");
    }
    return a / b;
}

function safeDivide(a, b) {
    try {
        const result = divide(a, b);
        console.log("結果:", result);
        return result;
    } catch (error) {
        console.error("錯誤:", error.message);
        return null;
    }
}

// 非同步錯誤處理
async function fetchData() {
    try {
        const response = await fetch('https://api.example.com/data');
        if (!response.ok) {
            throw new Error(`HTTP 錯誤! 狀態: ${response.status}`);
        }
        const data = await response.json();
        return data;
    } catch (error) {
        console.error("取得錯誤:", error.message);
        throw error; // 重新拋出
    }
}

safeDivide(10, 2); // 結果: 5
safeDivide(10, 0); // 錯誤: 除零錯誤
```

```go !! go
// Go: 錯誤處理
package main

import (
    "errors"
    "fmt"
    "net/http"
    "io/ioutil"
)

// 返回錯誤的函數
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("除零錯誤")
    }
    return a / b, nil
}

// 處理錯誤的函數
func safeDivide(a, b float64) {
    result, err := divide(a, b)
    if err != nil {
        fmt.Printf("錯誤: %v\n", err)
        return
    }
    fmt.Printf("結果: %v\n", result)
}

// 自訂錯誤型別
type ValidationError struct {
    Field   string
    Message string
}

func (e ValidationError) Error() string {
    return fmt.Sprintf("驗證錯誤 %s: %s", e.Field, e.Message)
}

// 帶自訂錯誤的函數
func validateAge(age int) error {
    if age < 0 {
        return ValidationError{Field: "age", Message: "年齡不能為負數"}
    }
    if age > 150 {
        return ValidationError{Field: "age", Message: "年齡似乎不現實"}
    }
    return nil
}

// 帶錯誤處理的 HTTP 請求
func fetchData(url string) ([]byte, error) {
    resp, err := http.Get(url)
    if err != nil {
        return nil, fmt.Errorf("取得資料失敗: %w", err)
    }
    defer resp.Body.Close()
    
    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("HTTP 錯誤! 狀態: %d", resp.StatusCode)
    }
    
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, fmt.Errorf("讀取回應失敗: %w", err)
    }
    
    return body, nil
}

func main() {
    safeDivide(10, 2) // 結果: 5
    safeDivide(10, 0) // 錯誤: 除零錯誤
    
    if err := validateAge(-5); err != nil {
        fmt.Printf("驗證錯誤: %v\n", err)
    }
    
    if err := validateAge(200); err != nil {
        fmt.Printf("驗證錯誤: %v\n", err)
    }
}
```
</UniversalEditor>

## 變數作用域對比

JavaScript 和 Go 在變數作用域方面有一些重要差異。

### 作用域規則對比

| 作用域類型 | JavaScript | Go | 說明 |
| :--------- | :---------- | :-- | :--- |
| **全域作用域** | `var x = 10;` | `var x = 10` (套件級別) | 都支援全域變數 |
| **函數作用域** | `function() { var x = 10; }` | `func() { x := 10 }` | 函數內部變數 |
| **區塊作用域** | `{ let x = 10; }` | `{ x := 10 }` | 花括號內變數 |
| **提升行為** | `var` 提升到函數頂部 | 無提升 | Go 變數必須在使用前宣告 |
| **閉包支援** | 完全支援 | 完全支援 | 都支援函數閉包 |
| **變數遮蔽** | 支援 | 支援 | 內層變數遮蔽外層 |
| **迴圈變數** | `for (let i = 0; ...)` | `for i := 0; ...` | 迴圈計數器作用域 |

### 變數生命週期對比

| 生命週期特性 | JavaScript | Go | 說明 |
| :----------- | :---------- | :-- | :--- |
| **垃圾回收** | 自動 | 自動 | 都使用垃圾回收 |
| **記憶體管理** | 自動 | 自動 | 開發者無需手動管理 |
| **變數銷毀** | 作用域結束時 | 作用域結束時 | 自動清理 |
| **參照計數** | 自動 | 自動 | 參照計數管理 |
| **記憶體洩漏** | 閉包可能導致 | 閉包可能導致 | 需要注意循環參照 |

## 基本資料型別對比

Go 有全面的內建型別，每種型別都有特定用途。

| JavaScript 型別 | Go 等價型別 | 說明 |
| :-------------- | :----------- | :--- |
| `number`        | `int`, `int8`, `int16`, `int32`, `int64`, `uint`, `uint8`, `uint16`, `uint32`, `uint64`, `float32`, `float64`, `complex64`, `complex128` | Go 有特定的整數大小 |
| `string`        | `string` | 預設 UTF-8 編碼 |
| `boolean`       | `bool` | 相同概念 |
| `null`          | `nil` | 用於指標、介面、切片、map、通道 |
| `undefined`     | 無直接等價 | 通常用 `nil` 或零值表示 |
| `object`        | `struct`, `map[string]interface{}` | 結構體是分組資料的主要方式 |
| `array`         | `[n]T` (陣列), `[]T` (切片) | 陣列固定大小，切片動態 |
| `symbol`        | 無直接等價 | Go 型別系統中不需要 |
| `bigint`        | `int64`, `uint64`, 或外部套件 | 用於任意精度 |

## 運算子和表達式

大多數算子相似，但 Go 在語法和行為上有一些差異。

<UniversalEditor title="算子對比" compare={true}>
```javascript !! js
// JavaScript: 算子
let a = 10, b = 3;

// 算術
let sum = a + b; // 13
let difference = a - b; // 7
let product = a * b; // 30
let quotient = a / b; // 3.333...
let remainder = a % b; // 1
let power = a ** b; // 1000

// 比較
let isEqual = (a === b); // false (嚴格相等)
let isLooseEqual = (a == b); // false (寬鬆相等)
let isGreater = (a > b); // true
let isLess = (a < b); // false

// 邏輯
let and = true && false; // false
let or = true || false; // true
let not = !true; // false

// 位元運算
let bitwiseAnd = a & b; // 2
let bitwiseOr = a | b; // 11
let bitwiseXor = a ^ b; // 9
let leftShift = a << 1; // 20
let rightShift = a >> 1; // 5

console.log(sum, difference, product, quotient, remainder, power);
```

```go !! go
// Go: 算子
package main

import (
    "fmt"
    "math"
)

func main() {
    a, b := 10, 3

    // 算術
    sum := a + b // 13
    difference := a - b // 7
    product := a * b // 30
    quotient := float64(a) / float64(b) // 3.333... (需要浮點轉換)
    remainder := a % b // 1
    power := math.Pow(float64(a), float64(b)) // 1000 (需要 math 套件)

    // 比較
    isEqual := (a == b) // false
    isGreater := (a > b) // true
    isLess := (a < b) // false

    // 邏輯
    and := true && false // false
    or := true || false // true
    not := !true // false

    // 位元運算
    bitwiseAnd := a & b // 2
    bitwiseOr := a | b // 11
    bitwiseXor := a ^ b // 9
    leftShift := a << 1 // 20
    rightShift := a >> 1 // 5

    fmt.Printf("算術: %d, %d, %d, %.2f, %d, %.0f\n", 
        sum, difference, product, quotient, remainder, power)
    fmt.Printf("比較: %t, %t, %t\n", isEqual, isGreater, isLess)
    fmt.Printf("邏輯: %t, %t, %t\n", and, or, not)
    fmt.Printf("位元運算: %d, %d, %d, %d, %d\n", 
        bitwiseAnd, bitwiseOr, bitwiseXor, leftShift, rightShift)
}
```
</UniversalEditor>

---

### 練習題：
1. 解釋 Go 中 `array` 和 `slice` 型別的區別，以及何時使用每種型別。
2. 編寫一個 Go 函數，接受整數切片並返回總和和平均值，演示多個返回值。
3. Go 的錯誤處理與 JavaScript 的異常處理有何不同？提供兩種方法的範例。
4. 建立一個 Go 結構體來表示 `Person`，包含姓名、年齡和郵箱欄位，然後編寫一個函數來驗證人員資料。

### 專案想法：
* 在 Go 中建構一個簡單的計算器程式，可以執行基本算術運算，演示函數使用、錯誤處理和使用者輸入處理。與 JavaScript 等效版本進行比較。

### 下一步：
* 學習 Go 的套件系統和模組管理
* 理解 Go 的型別系統和介面
* 探索 Go 強大的並發特性：goroutines 和 channels
